#ifndef _QANSILINE_H
#define _QANSILINE_H


#include "qansieditor.h"
#include <QtCore/QString>
#include <QtCore/QChar>
#include <QtCore/QByteArray>


namespace QAnsiEditor {


/**
 * This class represents an ANSI line, it contains colors and effects.
 *
 * @author gonwan (gonwan at gmail dot com)
 */
class QAnsiLine
{

public:
    /**
     * Constructor
     */
    QAnsiLine();
    /**
     * Constructor
     * @param strText The string content to set
     */
    QAnsiLine(/*in*/ const QString &strText);
    /**
     * Destructor
     */
    ~QAnsiLine();

    /**
     * Get length, in byte
     * @return The length
     */
    int getLength() const;
    /**
     * Get length, in character
     * @return The length
     */
    int getTextLength() const;

    /**
     * Get color at given index
     * @param iIndex The index in byte
     * @return The color
     */
    char getColor(/*in*/ int iIndex) const;
    /**
     * Set color at given index
     * @param iIndex The index in byte
     * @param chColor The color
     */
    void setColor(/*in*/ int iIndex, /*in*/ char chColor);
    /**
     * Get current color
     * @return The current color
     */
    char getCurrentColor() const;
    /**
     * Set current color
     * @param chColor The color to set
     */
    void setCurrentColor(/*in*/ char chColor);

    /**
     * Get effect at given index
     * @param iIndex The index in byte
     * @return The effect
     */
    char getEffect(/*in*/ int iIndex) const;
    /**
     * Set effect at given index
     * @param iIndex The index in byte
     * @param chEffect The effect
     */
    void setEffect(/*in*/ int iIndex, /*in*/ char chEffect);
    /**
     * Get current effect
     * @return The current effect
     */
    char getCurrentEffect() const;
    /**
     * Set current effect
     * @param chEffect The color to set
     */
    void setCurrentEffect(/*in*/ char chEffect);

    /**
     * Insert string at given index
     * If index <= -1, append string.
     * If index >= current length, additional space will be inserted.
     * Current color and effect will be used as its attributes.
     * @param strText The string to insert
     * @param iIndex The index in byte
     */
    void insertText(/*in*/ const QString &strText, /*in*/ int iIndex=-1);
    /**
     * Remove string at given index by given length
     * If index <= -1, delete whole string.
     * If index >= current length, no operation.
     * If length <= -1, delete the rest from index.
     * If the character at index is a partial character, the whole character will be deleted.
     * @param iIndex The index in byte
     * @param iLength The length in byte
     */
    void removeText(/*in*/ int iIndex=-1, /*in*/ int iLength=-1);
    /**
     * Replace using given string, at given index, by given length
     * If index <= -1, replace whole string.
     * If index >= current length, append string.
     * if length <= -1, replace string's length characters.
     * Current color and effect will be used as its attributes.
     * If the character at index is a partial character, the whole character will be replaced.
     * @param strText The string to replace
     * @param iIndex The index in byte
     * @param iLength The length in byte
     */
    void replaceText(/*in*/ const QString &strText, /*in*/ int iIndex=-1, /*in*/ int iLength=-1);
    /**
     * Get the plain string at given index by given length
     * If index <= -1, get whole string.
     * If length <= -1, get the rest from index.
     * If the character at index is a partial character, the whole character will be included.
     * @param iIndex The index in byte
     * @param iLength The length in byte
     * @return The string retrieved
     */
    QString getText(/*in*/ int iIndex=-1, /*in*/ int iLength=-1) const;
    /**
     * Get the ANSI string at given index by given length
     * It does not support one-char-two-color characters.
     * When it encounters a one-char-two-color characters, only the color of its first part is parsed.
     * It does not merge and compact ANSI escape sequence.
     * If index <= -1, get whole string.
     * If length <= -1, get the rest from index.
     * If the character at index is a partial character, the whole character will be included.
     * @param iIndex The index in byte
     * @param iLength The length in byte
     * @param strEscape Alternative ESC key
     * @return The string retrieved, which has ANSI sequences.
     */
    QString getAnsiText(/*in*/ int iIndex=-1, /*in*/ int iLength=-1, /*in*/ const QString &strEscape="\x1b\x1b") const;
    /**
     * Get the encoded ANSI string at given index by given length
     * It supports one-char-two-color characters, and we return a byte array.
     * It does not merge and compact ANSI escape sequence.
     * If index <= -1, get whole string.
     * If length <= -1, get the rest from index.
     * If the character at index is a partial character, the whole character will be included.
     * @param pCodec The codec used to convert to a byte array, if 0 is specified, a utf-8 encoded byte array is returned.
     * @param iIndex The index in byte
     * @param iLength The length in byte
     * @param strEscape Alternative ESC key
     * @return The byte array of string retrieved, which has ANSI sequences.
     */
    QByteArray getEncodedAnsiText(/*in*/ QTextCodec *pCodec, /*in*/ int iIndex=-1, /*in*/ int iLength=-1, /*in*/ const QString &strEscape="\x1b\x1b") const;
    /**
     * Insert ANSI string at given index
     * If index <= -1, append string.
     * If index >= current length, additional space will be inserted.
     * @param strText The string to insert
     * @param iIndex The index in byte
     */
    void insertAnsiText(/*in*/ const QAnsiLine &strAnsiText, /*in*/ int iIndex=-1);
    /**
     * Remove ANSI string at given index by given length
     * If index <= -1, delete whole string.
     * If index >= current length, no operation.
     * If length <= -1, delete the rest from index.
     * If the character at index is a partial character, the whole character will be deleted.
     * @param iIndex The index in byte
     * @param iLength The length in byte
     */
    void removeAnsiText(/*in*/ int iIndex=-1, /*in*/ int iLength=-1);
    /**
     * Replace using given ANSI string, at given index, by given length
     * If index <= -1, replace whole string.
     * If index >= current length, append string.
     * if length <= -1, replace string's length characters.
     * If the character at index is a partial character, the whole character will be replaced.
     * @param strText The string to replace
     * @param iIndex The index in byte
     * @param iLength The length in byte
     */
    void replaceAnsiText(/*in*/ const QAnsiLine &strAnsiText, /*in*/ int iIndex=-1, /*in*/ int iLength=-1);

    /**
     * Get changes in this line
     * If (start, end) is (-1, -1), the line has no change.
     * @param piStart The start position of changes, in byte
     * @param piEnd The end position of changes, in byte
     * @return true if has changes
     */
    bool getChanges(/*out*/ int *piStart, /*out*/ int *piEnd) const;
    /**
     * Set changes in this line
     * It also cache the value showing whether the line has blink characters.
     * If (start, end) is (-1, -1), mark the whole line to have changes.
     * @param iStart The start position of changes, in byte
     * @param iEnd The end position of changes, in byte
     */
    void setChanges(/*in*/ int iStart, /*in*/ int iEnd);
    /**
     * Clear all changes in this line
     */
    void clearChanges();

    /**
     * Whether has blinks
     * @return true if it has blinks
     */
    bool hasBlink() const;
    /**
     * Whether the character at given index is a partial character
     * @param iIndex The index in byte
     * @return true if it is a partial character
     */
    bool isPartial(/*in*/ int iIndex) const;
    /**
     * Get character size at given index
     * Single-byte returns 1, while Multi-bytes returns 2.
     * @param iIndex The index in byte
     * @return The size
     */
    int getCharSize(/*in*/ int iIndex) const;
    /**
     * Get byte index by given character index
     * @param iCharIndex The index in character
     * @return The byte index
     */
    int getByteIndex(/*in*/ int iCharIndex) const;

    /**
     * Help function to determine the width of a unicode character
     * @param ch The character
     * @return The width
     */
    static int calcCharWidth(/*in*/ QChar ch);

private:
    /** String content */
    QString m_strContent;
    /** Index array for mapping every byte of string and its content */
    QByteArray m_arrIndex;
    /** Color array mapping every byte of string content */
    QByteArray m_arrColors;
    /** Effect array mapping every byte of string content */
    QByteArray m_arrEffects;
    /** Whether the line has blink characters */
    bool m_bHasBlink;
    /** Current color for drawing */
    char m_chCurrentColor;
    /** Current effect for drawing */
    char m_chCurrentEffect;
    /** Mark start position of changes, default is -1 */
    int m_iChangeStart;
    /** Mark end position of changes, default is -1 */
    int m_iChangeEnd;

    /**
     * Help function to reset all states
     */
    void reset();
    /**
     * Help function to update the index array after assigning new string content
     */
    void updateIndex();
    /**
     * Help function to calculate real index and length, considering the content.
     * After calculation, the output index and length are aligned. They can not be in the middle of a character.
     * @param iIndex The input index
     * @param iLength The input length
     * @param piIndex The output index, can be 0
     * @param piLength The output length, can be 0
     * @return true if first 2 parameter are valid
     */
    bool calcRange(/*in*/ int iIndex, /*in*/ int iLength, /*out*/ int *piIndex, /*out*/ int *piLength) const;
    /**
     * Help function to construct the attribute prefix string at given index
     * @param iIndex The index in byte
     * @param strEscape Alternative ESC key
     * @return The attribute prefix string
     */
    QString getAttributeString(/*in*/ int iIndex, /*in*/ const QString &strEscape="\x1b\x1b") const;

};


}  // namespace QAnsiEditor


#endif  // _QANSILINE_H
